package org.shoukaiseki.common.utils;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageSerializable;
import org.shoukaiseki.common.function.IGetter;
import org.shoukaiseki.common.function.ISetter;
import org.shoukaiseki.common.logger.LoggerConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ClassUtils;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 *
 **/
public class BeanUtils{

    //用于获取方法对应的属性名
    public static final String get = "get" ;

    public static final String is = "is" ;

    public static final String set = "set" ;


    public static final Logger logger= LoggerFactory.getLogger(BeanUtils.class);

    public static <T,E,R> List<E> getFieldValueListByMapper(List<? extends T> list, Function<? super T, ? extends R> mapper){
        List collect = list.stream().filter(x -> x != null).map(mapper).filter(x -> x != null).collect(Collectors.toList());
        return collect;
    }

    public static void copyPropertiesSpring(Object source,Object target){
        org.springframework.beans.BeanUtils.copyProperties(source,target);
    }

    public static void copyProperties(Object source,Object target){
        try {
            org.apache.commons.beanutils.BeanUtils.copyProperties(target,source);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String getRelationshipNameFirstValueString(Object obj, String relationship){
        Set relationshipNameValueSet = getRelationshipNameValueSet(obj, relationship);
        Object o = relationshipNameValueSet.stream().findFirst().orElse(null);
        return o==null?null:o.toString();
    }


    /**
     * 获取关联属性值,存放到Set中
     * @param obj           要获取的
     * @param relationship  关联属性,存在子级使用 . 分割
     */
    public static Set getRelationshipNameValueSet(Object obj, String relationship){
        return  getMultiLevelChildFieldValueSet(obj,relationship.split("\\."));
    }

    //
    public static Set getMultiLevelChildFieldValueSet(Object obj, String... filedNames){
        Set list = new HashSet();
        Object temp = obj;

        for (int i = 0; i < filedNames.length; i++) {
            String fieldName = filedNames[i];
            if (temp instanceof Map) {
                temp = ((Map) temp).get(fieldName);
                if(i==filedNames.length-1){
                    list.addAll(valueToSet(temp));
                }
                continue;
            }

            if (temp instanceof PageSerializable) {
                temp = ((PageSerializable) temp).getList();
            }

            if (temp instanceof Collection) {
                list = new HashSet();
                for (Object source : (Collection) temp) {
                    PropertyDescriptor sourcePd = org.springframework.beans.BeanUtils.getPropertyDescriptor(source.getClass(), fieldName);
                    if (sourcePd != null) {
                        Method readMethod = sourcePd.getReadMethod();
                        if (readMethod != null &&
                                ClassUtils.isAssignable(readMethod.getReturnType(), readMethod.getReturnType())) {
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }
                            try {
                                Object value = readMethod.invoke(source);
                                list.addAll(valueToSet(value));
                            } catch (Throwable e) {
                                throw new RuntimeException("出错了",e);
                            }

                        }
                    }else{
                        if(source instanceof Map) {
                            Object value = ((Map) source).get(fieldName);
                            list.addAll(valueToSet(value));
                        }else{
                            logger.error("{}i={},fieldName={},temp{}", LoggerConstants.PREFIX,i,fieldName, temp);

                            throw new RuntimeException("错误[" + fieldName + "]");
                        }
                    }
                }
                //如果获取到的fieldName为List,则下次获取的字段名还是该名称重新循环一遍
                // 支持N级子表关联信息获取
                temp = list;
                if(logger.isDebugEnabled()){
                    logger.debug("temp={}", JSONObject.toJSONString(temp));
                }
                continue;
            }

            if (logger.isErrorEnabled()) {
                logger.error("{}i={},fieldName={},temp{}", LoggerConstants.PREFIX,i,fieldName, temp);
            }
            if(temp==null){
                if(i>0){
                    fieldName=filedNames[i-1];
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("可能relationship名称错误,找不到对应的可用值"+fieldName);
                }
                continue;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("可能relationship名称错误,无法识别的类型值"+fieldName);
            }
        }


        return list;
    }

    /**
     * 关联查询多个子表时,如果value为list,则将返回list内所有元素
     * @param value
     * @return
     */
    public static Set valueToSet(Object value){
        Set set = new HashSet();
        if(value==null){
            return set;
        }
        if (value instanceof PageSerializable) {
            set.addAll(valueToSet(((PageSerializable) value).getList()));
        }else if(value instanceof Set){
            for (Object o : ((Set) value)) {
                set.addAll(valueToSet(o));
            }
        }else if(value instanceof List){
            for (Object o : ((List) value)) {
                set.addAll(valueToSet(o));
            }
        }else{
            set.add(value);
        }


        return set;
    }



    /***
     * 转换方法引用为属性名
     * @param fn
     * @return
     */
    public static <T> String convertToFieldName(IGetter<T> fn) {
        SerializedLambda lambda = getSerializedLambda(fn);
        String methodName = lambda.getImplMethodName();
        String prefix = null;
        if(methodName.startsWith(get)){
            prefix = get;
        }
        else {
            if(methodName.startsWith(is)){
                prefix = is;
            }
        }
        if(prefix == null){
            logger.warn("无效的getter方法: "+methodName);
        }
        // 截取get/is之后的字符串并转换首字母为小写（S为diboot项目的字符串工具类，可自行实现）
//            return StringUtils.uncapFirst(S.substringAfter(methodName, prefix));
        return StringUtils.uncapitalize(StringUtils.substringAfter(methodName,prefix));
    }

    /***
     * 转换setter方法引用为属性名
     * @param fn
     * @return
     */
    public static <T,R> String convertToFieldName(ISetter<T,R> fn) {
        SerializedLambda lambda = getSerializedLambda(fn);
        String methodName = lambda.getImplMethodName();
        if(!methodName.startsWith(set)){
            logger.warn("无效的setter方法: "+methodName);
        }
        // 截取set之后的字符串并转换首字母为小写（S为diboot项目的字符串工具类，可自行实现）
//            return S.uncapFirst(S.substringAfter(methodName, "set"));
//            return methodName;
        return StringUtils.uncapitalize(StringUtils.substringAfter(methodName,"set"));
    }

    /***
     * 获取类对应的Lambda
     * @param fn
     * @return
     */
    private static SerializedLambda getSerializedLambda(Serializable fn){
        //先检查缓存中是否已存在
        SerializedLambda lambda = null;
        try{//提取SerializedLambda并缓存
            Method method = fn.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(Boolean.TRUE);
            //只是获取 SerializedLambda ,不会执行 lambda 方法
            lambda = (SerializedLambda) method.invoke(fn);
        }
        catch (Exception e){
            logger.error("获取SerializedLambda异常, class="+fn.getClass().getSimpleName(), e);
            throw new RuntimeException("获取SerializedLambda异常",e);
        }
        return lambda;
    }
}
